#include "mbed.h"
#include "rtos.h"
#include "remoteupdate.h"

typedef unsigned int UINT32;
	
static char recvMsg[1024];
static char buf[1024]; 
static int  pos = 0;

/* !!!!!!!!!!!!!!!NOTICE : Don't modify The following definitions!!!!!!!!!!!!!!! */
int updateUserDevType(char *devType) {
	const int ntests = 3;
    int i2c_stat = 0;
	const int i2c_delay_us = 5000;	//delay 5ms

	char data[2+HDR_NAME_32CHAR_LEN] = {0};
	int address = (UINT32)&header.userDevType - (UINT32)&header;
	data[0] = ((address&0xFF00) >> 8) & 0x00FF;
	data[1] = address&0x00FF;
	if (strlen(devType) <= HDR_NAME_32CHAR_LEN)
		strcpy(&data[2], devType);

	// Write stm32f411 header data to eeprom using i2c
	for (int i = 0; i < ntests; i++) {
		if ((i2c_stat = i2c.write(EEPROM_MEM_ADDR, data, HDR_NAME_32CHAR_LEN + 2)) != 0) {
			ERR("Unable to write userVersionNo to EEPROM (i2c_stat = 0x%02X), aborting", i2c_stat);
			continue;
		}

		// us delay if specified, Notice : This is necessary
		if (i2c_delay_us != 0)
			wait_us(i2c_delay_us);

		// ACK polling (assumes write will be successful eventually)
		while (i2c.write(EEPROM_MEM_ADDR, NULL, 0) != 0) ;

		if (strlen(devType) <= HDR_NAME_32CHAR_LEN)
			strcpy(header.userDevType, devType);
		return 0;
	}

	return -1;
}
int updateUserHardVerNo(char *priVer) {
	const int ntests = 3;
    int i2c_stat = 0;
	const int i2c_delay_us = 5000;	//delay 5ms

	char data[2+HDR_NAME_8CHAR_LEN] = {0};
	int address = (UINT32)&header.userHardVerNo - (UINT32)&header;
	data[0] = ((address&0xFF00) >> 8) & 0x00FF;
	data[1] = address&0x00FF;
	if (strlen(priVer) <= HDR_NAME_8CHAR_LEN)
		strcpy(&data[2], priVer);

	// Write stm32f411 header data to eeprom using i2c
	for (int i = 0; i < ntests; i++) {
		if ((i2c_stat = i2c.write(EEPROM_MEM_ADDR, data, HDR_NAME_8CHAR_LEN + 2)) != 0) {
			ERR("Unable to write userVersionNo to EEPROM (i2c_stat = 0x%02X), aborting", i2c_stat);
			continue;
		}

		// us delay if specified, Notice : This is necessary
		if (i2c_delay_us != 0)
			wait_us(i2c_delay_us);

		// ACK polling (assumes write will be successful eventually)
		while (i2c.write(EEPROM_MEM_ADDR, NULL, 0) != 0) ;

		if (strlen(priVer) <= HDR_NAME_8CHAR_LEN)
			strcpy(header.userHardVerNo, priVer);
		return 0;
	}

	return -1;
}
int updateUserVersionNo(char *version) {
	const int ntests = 3;
    int i2c_stat = 0;
	const int i2c_delay_us = 5000;	//delay 5ms

	char data[2+HDR_NAME_12CHAR_LEN] = {0};
	int address = (UINT32)&header.userSoftVerNo - (UINT32)&header;
	data[0] = ((address&0xFF00) >> 8) & 0x00FF;
	data[1] = address&0x00FF;
	if (strlen(version) <= HDR_NAME_12CHAR_LEN)
		strcpy(&data[2], version);

	// Write stm32f411 header data to eeprom using i2c
	for (int i = 0; i < ntests; i++) {
		if ((i2c_stat = i2c.write(EEPROM_MEM_ADDR, data, HDR_NAME_12CHAR_LEN + 2)) != 0) {
			ERR("Unable to write userVersionNo to EEPROM (i2c_stat = 0x%02X), aborting", i2c_stat);
			continue;
		}

		// us delay if specified, Notice : This is necessary
		if (i2c_delay_us != 0)
			wait_us(i2c_delay_us);

		// ACK polling (assumes write will be successful eventually)
		while (i2c.write(EEPROM_MEM_ADDR, NULL, 0) != 0) ;

		if (strlen(version) <= HDR_NAME_12CHAR_LEN)
			strcpy(header.userSoftVerNo, version);
		return 0;
	}

	return -1;
}

int getUpdateFileInfo(char *info, char len) {
	const int ntests = 3;
	int i2c_stat = 0;

	// Write stm32f411 header data to eeprom using i2c
	for (int i = 0; i < ntests; i++) {
		//write the address you want to read
		char data[2] = {0};
		int address = (UINT32)&header.userUpdateFileInfo - (UINT32)&header;
		data[0] = ((address&0xFF00) >> 8) & 0x00FF;
		data[1] = address&0x00FF;
		if ((i2c_stat = i2c.write(EEPROM_MEM_ADDR, data, 2, true)) != 0) {
			INFO("Unable to write userUpdateFileInfo addr to EEPROM (i2c_stat = 0x%02X), aborting", i2c_stat);
			continue;
		}

		/* read the eeprom using i2c */
		if ((i2c_stat = i2c.read(EEPROM_MEM_ADDR, info, len)) != 0) {
			INFO("Read userUpdateFileInfo failed(%d) at read, i2c_stat is 0x%02X", i, i2c_stat);
			continue;
		}
		return 0;
	}

	return -1;
}

bool setUpdateFileInfo(char flag) {
	const int ntests = 3;
	int i2c_stat = 0;
	const int i2c_delay_us = 5000;	//delay 5ms

	char data[2+HDR_NAME_20CHAR_LEN] = {0};
	int address = (UINT32)&header.userUpdateFileInfo - (UINT32)&header;
	data[0] = ((address&0xFF00) >> 8) & 0x00FF;
	data[1] = address&0x00FF;
	data[2] = flag;
	if (flag == 1) {
		for (int i = 0; i < 4; i++)
			data[2+(HDR_NAME_4CHAR_LEN)+i] = (updateinfo.fileLength>>(i*8))&0xFF;

		sprintf(&data[2+(HDR_NAME_8CHAR_LEN)], "%s", updateinfo.newVersionNo);
	}

	// Write stm32f411 header data to eeprom using i2c
	for (int i = 0; i < ntests; i++) {
		if ((i2c_stat = i2c.write(EEPROM_MEM_ADDR, data, 2+HDR_NAME_20CHAR_LEN)) != 0) {
			INFO("Unable to write userUpdateFileInfo to EEPROM (i2c_stat = 0x%02X), aborting", i2c_stat);
			continue;
		}

		// us delay if specified, Notice : This is necessary
		if (i2c_delay_us != 0)
			wait_us(i2c_delay_us);

		// ACK polling (assumes write will be successful eventually)
		while (i2c.write(EEPROM_MEM_ADDR, NULL, 0) != 0) ;

		return true;
	}

	return false;
}

bool setSelfUpdateFileInfo(char flag) {
	const int ntests = 3;
	int i2c_stat = 0;
	const int i2c_delay_us = 5000;	//delay 5ms

	char data[2+HDR_NAME_20CHAR_LEN] = {0};
	int address = (uint32_t)&header.selfUpdateFileInfo - (uint32_t)&header;
	data[0] = ((address&0xFF00) >> 8) & 0x00FF;
	data[1] = address&0x00FF;
	data[2] = flag;
	data[3] = selfUpdateinfo.addrNum;
	if (flag == 1) {
		for (int i = 0; i < 4; i++)
			data[2+(HDR_NAME_4CHAR_LEN)+i] = (selfUpdateinfo.fileLength>>(i*8))&0xFF;

		sprintf(&data[2+(HDR_NAME_8CHAR_LEN)], "%s", selfUpdateinfo.newVersionNo);
	} else if (flag == 2) {
		for (int i = 0; i < 4; i++)
			data[2+(HDR_NAME_4CHAR_LEN)+i] = ((SELF_IMG_MAP_BUF_SIZE)>>(i*8))&0xFF;

		sprintf(&data[2+(HDR_NAME_8CHAR_LEN)], "%s", header.softVerNo);
	} else if (flag == 0) {
		data[3] = header.selfUpdateFileInfo[1];
	}

	// Write stm32f411 header data to eeprom using i2c
	for (int i = 0; i < ntests; i++) {
		if ((i2c_stat = i2c.write(EEPROM_MEM_ADDR, data, 2+HDR_NAME_20CHAR_LEN)) != 0) {
			INFO("Unable to write selfUpdateFileInfo to EEPROM (i2c_stat = 0x%02X), aborting\r\n", i2c_stat);
			continue;
		}

		// us delay if specified, Notice : This is necessary
		if (i2c_delay_us != 0)
			wait_us(i2c_delay_us);

		// ACK polling (assumes write will be successful eventually)
		while (i2c.write(EEPROM_MEM_ADDR, NULL, 0) != 0) ;

		return true;
	}

	return false;
}
/* !!!!!!!!!!!!!!!NOTICE : Don't modify The upper definitions!!!!!!!!!!!!!!! */

int erase_magic() {
	struct stm32f411xx_baseboard_id header;
	memset(&header, 0, sizeof(struct stm32f411xx_baseboard_id));
	if (write_eeprom(&header) < 0) {
		ERR("Erase eeprom failure!");
//		return -1;
	}

	return 0;
}

/*
 * Read header information from EEPROM into global structure.
 */
int read_eeprom(struct stm32f411xx_baseboard_id *header) {
	const int ntests = 10;
    int i2c_stat = 0;

	// Read the header from eeprotm using i2c
	for (int i = 0; i < ntests; i++) {
		//write the address you want to read
		char data[] = { 0, 0 };
		if ((i2c_stat = i2c.write(EEPROM_MEM_ADDR, data, 2, true)) != 0) {
			ERR("Test %d failed at write, i2c_stat is 0x%02X", i, i2c_stat);
			continue;
		}

		/* read the eeprom using i2c */
		if ((i2c_stat = i2c.read(EEPROM_MEM_ADDR, (char *)header, sizeof(struct stm32f411xx_baseboard_id))) != 0) {
			ERR("Test %d failed at read, i2c_stat is 0x%02X", i, i2c_stat);
			continue;
		}
		break;
	}

#if MY_DEBUG
	if (header->magic != 0x04030201) {
#else
	if (header->magic != DEVICE_MAGIC_ID) {
#endif
		header->magic = DEVICE_MAGIC_ID;
		strcpy(header->devId, "1234567890");
		strcpy(header->serverIp, REMOTE_COAP_SERVER_IP);
		sprintf(header->serverPort, "%d", REMOTE_COAP_SERVER_PORT);

		strcpy(header->devType, DEVICE_TYPE);
		memset(header->reserve1, 0, 4);
		strcpy(header->softVerNo, DEVICE_SOFTWARE_VERSION);
		strcpy(header->hardVerNo, DEVICE_HARDWARE_VERSION);
		strcpy(header->firmwareNo, DEVICE_FIRMWARE_VERSION);
		memset(header->selfUpdateFileInfo, 0, HDR_NAME_20CHAR_LEN);

		strcpy(header->userSoftVerNo, USER_DEVICE_SOFTWARE_VERSION);
		memset(header->reserve2, 0, 4);
		strcpy(header->userDevType, USER_DEVICE_TYPE);
		strcpy(header->userHardVerNo, USER_DEVICE_HARDWARE_VERSION);
		memset(header->userUpdateFileInfo, 0, HDR_NAME_20CHAR_LEN);
		memset(header->reserve3, 0, 4);
		strcpy(header->smsPassWord, "admin");
		header->bitDelay = 50;
		header->startTiming = 0;
		header->closeTiming = 0;

		return -1;
	}

	return 0;
}
/*
 * Write header information from EEPROM into global structure.
 */
int write_eeprom(struct stm32f411xx_baseboard_id *header)
{
	const int ntests = 1;
	char flag = 0;
    int i2c_stat = 0;
	const int i2c_delay_us = 5000;	//delay 5ms
	int integer = sizeof(struct stm32f411xx_baseboard_id)/EEPROM_PAGE_SIZE;
	int remainder = sizeof(struct stm32f411xx_baseboard_id)%EEPROM_PAGE_SIZE;
	int num = remainder?(integer+1):integer;

	// Write stm32f411 header data to eeprom using i2c
	for (int i = 0; i < ntests; i++) {
		for (int page = 0; page < num; page++) {
			char data[2+EEPROM_PAGE_SIZE] = {0};
			int address = page*EEPROM_PAGE_SIZE;
			char len = (page+1 == num)?(remainder?remainder:(EEPROM_PAGE_SIZE)):(EEPROM_PAGE_SIZE);
			data[0] = ((address&0xFF00) >> 8) & 0x00FF;
			data[1] = address&0x00FF;
			memcpy(&data[2], (char *)header+address, len);

			if ((i2c_stat = i2c.write(EEPROM_MEM_ADDR, data, len + 2)) != 0) {
				ERR("Unable to write data to EEPROM (i2c_stat = 0x%02X), aborting", i2c_stat);
				flag = 1;
				break;
			} else
				flag = 0;

			// us delay if specified, Notice : This is necessary
			if (i2c_delay_us != 0)
				wait_us(i2c_delay_us);

			// ACK polling (assumes write will be successful eventually)
			while (i2c.write(EEPROM_MEM_ADDR, NULL, 0) != 0) ;
		}

		if (flag == 0)
			return 0;
	}

	return -1;
}

// This function is called when a character goes into the RX buffer.
void rxCallback() {
	char cGet;
	cGet = userCom.getc();
	if (cGet != '$' && pos < 1024) {
		recvMsg[pos++] = cGet;
	}
	else {
		memset(buf, 0, sizeof(buf));
		strncpy(buf, recvMsg, pos);
		pos = 0;
		memset(recvMsg, 0, sizeof(recvMsg));
	}
}

int update_eeprom(struct stm32f411xx_baseboard_id *header)
{
	char *tmpStr = NULL;
	char str[32];
	
	if (!strlen(buf))
		return -1;
	else 
		userCom.printf(SUCCESS);
	
	printf("recvMsg=%s\r\n",buf);

	header->magic = DEVICE_MAGIC_ID;

	memset(str, 0 , sizeof(str));
	tmpStr=strstr(buf, "DEVICE_ID=");
	if (tmpStr != NULL) {
		sscanf(tmpStr+10, "%[^ ]", str);
		memcpy(header->devId, str, HDR_NAME_12CHAR_LEN*2);
	}
	
	memset(str, 0 , sizeof(str));
	tmpStr=strstr(buf, "REMOTE_COAP_SERVER_IP=");
	if (tmpStr != NULL) {
		sscanf(tmpStr+22, "%[^ ]", str);
		memcpy(header->serverIp, str, HDR_NAME_8CHAR_LEN*2);
	}
	
	memset(str, 0 , sizeof(str));
	tmpStr=strstr(buf, "REMOTE_COAP_SERVER_PORT=");
	if (tmpStr != NULL) {
		sscanf(tmpStr+24, "%[^ ]", str);
		memcpy(header->serverPort, str, HDR_NAME_8CHAR_LEN);
	}
	
	memset(str, 0 , sizeof(str));
	tmpStr=strstr(buf, "DEVICE_TYPE=");
	if (tmpStr != NULL) {
		sscanf(tmpStr+12, "%[^ ]", str);
		memcpy(header->devType, str, HDR_NAME_8CHAR_LEN);
	}

	memset(str, 0 , sizeof(str));
	tmpStr=strstr(buf, "DEVICE_SOFTWARE_VERSION=");
	if (tmpStr != NULL) {
		sscanf(tmpStr+24, "%[^ ]", str);
		memcpy(header->softVerNo, str, HDR_NAME_12CHAR_LEN);
	}
	
	memset(str, 0 , sizeof(str));
	tmpStr=strstr(buf, "DEVICE_HARDWARE_VERSION=");
	if (tmpStr != NULL) {
		sscanf(tmpStr+24, "%[^ ]", str);
		memcpy(header->hardVerNo, str, HDR_NAME_8CHAR_LEN);
	}
	
	memset(str, 0 , sizeof(str));
	tmpStr=strstr(buf, "DEVICE_FIRMWARE_VERSION=");
	if (tmpStr != NULL) {
		sscanf(tmpStr+24, "%[^ ]", str);
		memcpy(header->firmwareNo, str, HDR_NAME_8CHAR_LEN);
	}
	
	memset(str, 0 , sizeof(str));
	tmpStr=strstr(buf, "USER_DEVICE_TYPE=");
	if (tmpStr != NULL) {
		sscanf(tmpStr+17, "%[^ ]", str);
		memcpy(header->userDevType, str, HDR_NAME_32CHAR_LEN);
	}
	
	memset(str, 0 , sizeof(str));
	tmpStr=strstr(buf, "USER_DEVICE_SOFTWARE_VERSION=");
	if (tmpStr != NULL) {
		sscanf(tmpStr+29, "%[^ ]", str);
		memcpy(header->userSoftVerNo, str, HDR_NAME_12CHAR_LEN);
	}
	
	memset(str, 0 , sizeof(str));
	tmpStr=strstr(buf, "USER_DEVICE_HARDWARE_VERSION=");
	if (tmpStr != NULL) {
		sscanf(tmpStr+29, "%[^ ]", str);
		memcpy(header->userHardVerNo, str, HDR_NAME_8CHAR_LEN);
	}
	
	memset(str, 0 , sizeof(str));
	tmpStr=strstr(buf, "SMS_PASSWORD=");
	if (tmpStr != NULL) {
		sscanf(tmpStr+13, "%[^ ]", str);
		memcpy(header->smsPassWord, str, HDR_NAME_8CHAR_LEN);
	}
	
	memset(str, 0 , sizeof(str));
	tmpStr=strstr(buf, "BIT_DELAY=");
	if (tmpStr != NULL) {
		sscanf(tmpStr+10, "%[^ ]", str);
		header->bitDelay=atoi(str);
	}
	
	memset(str, 0 , sizeof(str));
	tmpStr=strstr(buf, "START_TIMING=");
	if (tmpStr != NULL) {
		sscanf(tmpStr+13, "%[^ ]", str);
		header->startTiming = strtol(str, NULL, 16);
	}
	
	memset(str, 0 , sizeof(str));
	tmpStr=strstr(buf, "CLOSE_TIMING=");
	if (tmpStr != NULL) {
		sscanf(tmpStr+13, "%[^ ]", str);
		header->closeTiming = strtol(str, NULL, 16);
	}

	memset(header->selfUpdateFileInfo, 0, sizeof(header->selfUpdateFileInfo));
	memset(header->userUpdateFileInfo, 0, sizeof(header->userUpdateFileInfo));
	memset(header->reserve1, 0 , sizeof(header->reserve1));
	memset(header->reserve2, 0 , sizeof(header->reserve2));
	memset(header->reserve3, 0 , sizeof(header->reserve3));
	memset(buf, 0, sizeof(buf));

	return 0;	
}

void reset4GModem() 
{
	DigitalOut resin_n(PC_8);
	resin_n = 0;
	resin_n = 1;
	Thread::wait(60);
	resin_n = 0;
}

void SoftReset()
{
	__set_FAULTMASK(1);
	NVIC_SystemReset();
}
